<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>导航守卫</title>
    <script src="https://unpkg.com/vue@2.7.7/dist/vue.js"></script>
    <script src="https://unpkg.com/vue-router@3.6.5/dist/vue-router.js"></script>
  </head>
  <body>
    <!-- 1 全局前置守卫 -->
    <div id="app1" style="border: 1px dashed red;margin-bottom: 2px;">
      <router-link to="/foo">to /foo</router-link><br />
      <router-link to="/user/a001">to /user/a001</router-link><br />
      <router-link to="/user/a002">to /user/a002</router-link><br />
      <router-view></router-view>
    </div>

    <script type="text/javascript">
      // 1 全局前置守卫
      const User = {template: '<div>User: {{$route.params.id}}</div>'}
      const NotFoundComponent = {template: '<div>NotFound</div>'}
      var isAuthenticated = true;
      const Login = {template: "<div>Login: <br />Username:<input type='text'/> <br />Password:<input type='password'/> <br /></div>"}
      
      // 5 组件内的守卫
      const Foo = {
        template: '<div>Foo</div>',
        beforeRouteEnter(to, from, next) {
          // 在渲染该组件的对应路由被 confirm 前调用
          // 不！能！获取组件实例 `this`
          // 因为当守卫执行前，组件实例还没被创建
          
          //   beforeRouteEnter 守卫 不能 访问 this，因为守卫在导航确认前被调用，因此即将登场的新组件还没被创建。不过，你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调，并且把组件实例作为回调方法的参数。
          next(vm => {
            // 通过 `vm` 访问组件实例
            console.log('beforeRouteEnter: vm: ' + vm)
          })
        },
        beforeRouteUpdate(to,from,next) {
          // 在当前路由改变，但是该组件被复用时调用
          // 举例来说，对于一个带有动态参数的路径 /foo/:id，在 /foo/1 和 /foo/2 之间跳转的时候，
          // 由于会渲染同样的 Foo 组件，因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
          // 可以访问组件实例 `this`
          
          //   注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdate 和 beforeRouteLeave 来说，this 已经可用了，所以不支持传递回调，因为没有必要了。
          this.name = to.params.name
          next()
        },
       beforeRouteLeave(to,from,next) {
          // 导航离开该组件的对应路由时调用
          // 可以访问组件实例 `this`

          //   这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。
          const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
          if (answer) next()
          else next(false)
        }
      }
      
      const router = new VueRouter({
        routes: [
          {
            path: '/user/:id', 
            component: User,
            // 4 路由独享的守卫：你可以在路由配置上直接定义 beforeEnter 守卫：
            beforeEnter: (to, from, next) => {
              // ...
              console.log('beforeEnter: to.path: ' + to.path)
              console.log('beforeEnter: from.path: ' + from.name)
              next()
            }
          },
          {name: 'Foo', path: '/foo', component: Foo},
          {name: 'Login', path: '/login', component: Login},
          // 警告
          {path: '*', component: NotFoundComponent}
        ]
      })
      
      router.beforeEach((to, from, next) => {
        if (to.name != 'Login' && !isAuthenticated) next({name: 'Login'})
        // 如果用户未能验证身份，则 `next` 会被调用两次，所以这里必须使用 else 关键字！
        else next()
      })
      
      const vm1 = new Vue({
        el: '#app1',
        router
      })
      

      // 2 全局解析守卫
      //   在 2.5.0+ 你可以用 router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似，区别是在导航被确认之前，同时在所有组件内守卫和异步路由组件被解析之后，解析守卫就被调用。

      // 3 全局后置钩子
      //   你也可以注册全局后置钩子，然而和守卫不同的是，这些钩子不会接受 next 函数也不会改变导航本身：
      router.afterEach((to, from) => {
        // ...
        console.log('afterEach: to.path: ' + to.path)
        console.log('afterEach: from.path: ' + from.path)
      })

      // 6 完整的导航解析流程
        /* 
        1 导航被触发。
        2 在失活的组件里调用 beforeRouteLeave 守卫。
        3 调用全局的 beforeEach 守卫。
        4 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
        5 在路由配置里调用 beforeEnter。
        6 解析异步路由组件。
        7 在被激活的组件里调用 beforeRouteEnter。
        8 调用全局的 beforeResolve 守卫 (2.5+)。
        9 导航被确认。
        10 调用全局的 afterEach 钩子。
        11 触发 DOM 更新。
        12 调用 beforeRouteEnter 守卫中传给 next 的回调函数，创建好的组件实例会作为回调函数的参数传入。 
        */
    </script>
  </body>
</html>